import os
import argparse
import re
import glob
from pathlib import Path

# example usages:
# python localizations.py generate
# python localizations.py validate

parser = argparse.ArgumentParser()
parser.add_argument('command', type=str, help="Possible options: generate | validate")
arguments = parser.parse_args()

command = arguments.command
if command not in ['generate', 'validate']:
    parser.print_help()
    exit()

input_file_name = Path("Support/en.lproj/Localizable.strings")
comment_file_name = Path("Support/qqq.lproj/Localizable.strings")
enum_name = "LocalString"
target_dir = "Support"
template_file_name = "StringLocalExtension.swift_temp"

class Generate:
    def __init__(self, input_file_name, template_file, enum_name, target_dir):
        self.enum_name = enum_name
        reader = Reader(input_file_name)
        vars = self.__variables(reader)
        
        with open(os.path.join(target_dir, template_file)) as template_file:
            template_content = template_file.read()
        
        output_path = os.path.join(target_dir, enum_name + ".swift")
        file = open(output_path, 'w')
        file.write("""//
// !! DO NOT EDIT THIS FILE DIRECTLY !! 
// !! IT HAS BEEN AUTO-GENERATED !!
//
""")
        file.write(template_content)
        file.write(self.__code_for("\n\t".join(vars)))
        file.close()
        
    def __variables(self, reader):
        vars = list()
        for key, has_arguments in sorted(reader.keys()):
            if has_arguments:
                vars.append(self.__static_func_for(key))
            else:
                vars.append(self.__static_let_for(key))
        return vars
    
    def __code_for(self, variables):
        return """enum {} {{
    {}
}}
""".format(self.enum_name, variables)

    def __static_let_for(self, key):
        return """static let {} = "{}".localized""".format(self.__get_var_name(key), key)
    
    def __static_func_for(self, key):
        return """static func {}(withArgs args: CVarArg...) -> String {{ "{}".localizedWithFormat(withArgs: args) }}""".format(self.__get_var_name(key), key)

    def __get_var_name(self, key):
        return re.sub('[^a-z0-9]', '_', key.lower())
    

class Reader:
    def __init__(self, input_file_name):
        self.input_file_name = input_file_name

    def keys(self):
        pattern = re.compile(r'"(?P<key>.+)"\s{1,}=\s{1,}"(?P<value>.+)"')
        with open(self.input_file_name) as input_file:
            for line in input_file:
                match = pattern.match(line)
                if match:
                    groups = match.groupdict()
                    key = groups.get('key')
                    value = groups.get('value')
                    has_arguments = "%@" in value
                    yield key, has_arguments

class Validate:
    def __init__(self, input_file_name, comment_file_name, enum_name, search_directory=os.getcwd()):
        reader = Reader(input_file_name)
        vars = list()
        for key, _ in reader.keys():
            assert "." in key, "Invalid translation key: {}, it should contain at least one '.' (dot)".format(key)
            vars.append(key)
            
        vars = sorted(vars)
        
        matches = dict()
        counter = dict()
        for var in vars:
            swift_var = self.__get_var_name(var)
            counter[swift_var] = 0
        
        for swift_file_name in glob.iglob(os.path.join(search_directory, '**/*.swift'), recursive=True):
            if Path(swift_file_name).name != "{}.swift".format(enum_name):
                with open(swift_file_name, 'r') as swift_file:
                    content = swift_file.read()
                    for var in vars:
                        if var in content:
                            if var in matches:
                                matches[var].append(swift_file_name)
                            else:
                                matches[var] = [swift_file_name]
                        swift_var = self.__get_var_name(var)
                        if swift_var in content:
                            counter[swift_var] += 1
                    
        assert len(matches.keys()) == 0, "localization strings cannot not be directly used in swift (use LocalString instead): {}".format(matches)
        unused_swift_vars = {k: v for k, v in counter.items() if v == 0 }.keys()
        assert len(unused_swift_vars) == 0, "unused localizations entries (delete them from localizations, and run: python localizations.py generate): {}".format(sorted(unused_swift_vars))
        
        comment_keys = list()
        comment_reader = Reader(comment_file_name)
        for comment_key, _ in comment_reader.keys():
            assert comment_key in vars, "extra qqq key found: {}".format(comment_key)
            comment_keys.append(comment_key)
        
        missing = sorted(set(vars).difference(comment_keys))
        assert len(missing) == 0, "undocumented keys (please add them to qqq): {}".format(missing)
        
    def __get_var_name(self, key):
        return re.sub('[^a-z0-9]', '_', key.lower())

match command:
    case "generate":
        Generate(input_file_name, template_file_name, enum_name, target_dir)
    case "validate":
        Validate(input_file_name, comment_file_name, enum_name)
    case _:
        exit(-1)